package ren.chemi.image;


import java.util.Map;
import java.util.HashMap;
import java.awt.image.BufferedImage;

/**
 * @author zengxiangwei
 * 图像颜色特征向量提取， 计算特征向量相关性
 */
public class ImageColorHistogramSimilarity {

    /**
     * 颜色最大值
     */
    static final int COLOR_MAX_VALUE = 255;

    /**
     * 颜色最小值
     */
    static final int COLOR_MIN_VALUE = 0;

    /**
     * 单个通道上的的取值数量
     */
    static final int CHANNEL_STEP_SIZE = 8;

    /**
     * 颜色值跨值范围
     */
    static final int CHANNEL_STEP = (COLOR_MAX_VALUE + 1) / CHANNEL_STEP_SIZE;

    /**
     * 整个特征向量的大小
     */
    static final int VECTOR_SIZE = CHANNEL_STEP_SIZE * CHANNEL_STEP_SIZE * CHANNEL_STEP_SIZE;

    /**
     * 计算两个特征向量的相识度
     * 得到相关系数的值介于–1与+1之间，即–1≤r≤+1。其性质如下：
     *
     * 当r>0时，表示两变量正相关，r<0时，两变量为负相关。
     * 当|r|=1时，表示两变量为完全线性相关，即为函数关系。
     * 当r=0时，表示两变量间无线性相关关系。
     * 当0<|r|<1时，表示两变量存在一定程度的线性相关。且|r|越接近1，两变量间线性关系越密切；|r|越接近于0，表示两变量的线性相关越弱。
     * 一般可按三级划分：|r|<0.4为低度线性相关；0.4≤|r|<0.7为显著性相关；0.7≤|r|<1为高度线性相关。
     * @param vector_a vector a
     * @param vector_b vector b
     * @return similarity 0 ~ 1.0
     */
    static public double colorHistogramSimilarity(int[] vector_a, int[] vector_b) {

        if(vector_a == null || vector_b == null || vector_a.length != vector_b.length){
            return 0.0;
        }

        double sumX = 0.0;
        double sumY = 0.0;
        double sumX_Sq = 0.0;
        double sumY_Sq = 0.0;
        double sumXY = 0.0;

        int N = vector_a.length;
        for (int i = 0; i < N; i++) {
            int x = vector_a[i];
            int y = vector_b[i];
            sumX += x;
            sumY += y;
            sumX_Sq += Math.pow(x, 2);
            sumY_Sq += Math.pow(y, 2);
            sumXY += x * y;
        }

        double numerator = sumXY - sumX * sumY / N;
        double denominator = Math.sqrt((sumX_Sq - sumX * sumX / N) * (sumY_Sq - sumY * sumY / N));

        if (denominator == 0) {
            return 0.0;
        }

        return Math.abs(numerator / denominator);
    }

    /**
     * 得到图片的颜色特征向量
     * @param src
     * @return int [] 颜色特征向量， 长度为 VECTOR_SIZE, 默认64
     */
    static public int[] colorHistogramFeatherVector(BufferedImage src) {
        int width = src.getWidth();
        int height = src.getHeight();
        Map<String, Integer> imageHistogram = new HashMap<String, Integer>();
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                int rgb = src.getRGB(j, i);

                // 取出次高位（16-23）红色分量的信息
                int r = (rgb >> 16) & 0xff;
                // 取出中位（8-15）绿色分量的信息
                int g = (rgb >> 8) & 0xff;
                //取出低位（0-7）蓝色分量的信息
                int b = rgb & 0xff;
                String key = getRange(r) + getRange(g) + getRange(b);

                Integer count = imageHistogram.get(key);
                if (count == null) {
                    imageHistogram.put(key, 1);
                } else {
                    imageHistogram.put(key, count + 1);
                }
            }
        }


        int[] result = new int[VECTOR_SIZE];
        String key = "";
        int idx = 0;
        for (int r = 0; r < CHANNEL_STEP_SIZE; r++) {
            for (int g = 0; g < CHANNEL_STEP_SIZE; g++) {
                for (int b = 0; b < CHANNEL_STEP_SIZE; b++) {
                    key = String.valueOf(r) + g + b;
                    Integer count = imageHistogram.get(key);
                    if (count == null) {
                        result[idx] = 0;
                    } else {
                        result[idx] = count;
                    }
                    idx++;
                }
            }
        }
        return result;
    }

    /**
     * 统一图像大小提取特征向量，便于量化比较
     * @param image
     * @return
     */
    public static int [] getImageFeatherVector(BufferedImage image){
        final int target_width = 320;
        final int target_height = 340;
        BufferedImage resizeImage = Scalr.resize(image, target_width, target_height);
        return ImageColorHistogramSimilarity.colorHistogramFeatherVector(resizeImage);
    }

    static private String getRange(int color) {
        if (color >= COLOR_MIN_VALUE && color <= COLOR_MAX_VALUE) {
            return String.valueOf(color / CHANNEL_STEP);
        }
        return null;
    }
}
